#include "face.h"
#include "loop.h"
#include "vertex.h"
#include "mergesort.h"

Face::Face(unsigned int _id) : id(_id){
    loop = NULL;
    solid = NULL;
    selected = false;
    bezier = NULL;
}

Face::Face(unsigned int _id, Solid *_s, Loop* _l) : id(_id), solid(_s), loop(_l){
    selected = false;
    bezier = NULL;
}

Face::~Face(){
    delete loop;
    if (!(bezier == NULL)) delete bezier;
}

void Face::setSolid(Solid *_s){
    solid = _s;
}

void Face::setLoop(Loop *_l){
    loop = _l;
}

void Face::setBezier(int _n, double _d){
    bezier = new BezierSurface(_n, _d);

    /*TODO - Se a face não tiver quatro pontos*/
    Point p0 = loop->getHERing()->getStart()->getPoint();
    Point p1 = loop->getHERing()->getNext()->getStart()->getPoint();
    Point p2 = loop->getHERing()->getNext()->getNext()->getStart()->getPoint();
    Point p3 = loop->getHERing()->getNext()->getNext()->getNext()->getStart()->getPoint();
    Point p, q;
    Point *f;
    for (int i = 0; i < _n; i++){
        p = p0 + (p3 - p0)*(i/(_n - 1.0));
        q = p1 + (p2 - p1)*((double)(i/(_n - 1.0)));
        for (int j = 0; j < _n; j++){
            f = new Point (p + (q - p)*((double)(j/(_n - 1.0))));
            bezier->addBezierPoint(j, i, f);
        }
    }
    bezier->calculate();
}

unsigned int Face::getID(){
    return id;
}

Solid *Face::getSolid(){
    return solid;
}

Loop *Face::getLoop(){
    return loop;
}

BezierSurface *Face::getBezier(){
    return bezier;
}

double *Face::getNormalVector(){
    return getNormal().intoVector();
}

Point Face::getNormal(){
    HalfEdge *he = loop->getHERing();
    Point p = ( he->getNext()->getStart()->getPoint() - he->getStart()->getPoint() ).crossProduct( he->getPrev()->getStart()->getPoint() - he->getStart()->getPoint() );

    return p.normalization();
}

Point Face::getBoundingBoxCenter(std::vector<Vertex *> vertices){
    int n = vertices.size();
    Point p;
    double xPosition[n], yPosition[n], zPosition[n];
    for(int i = 0; i < n; i++){
        p = vertices.at(i)->getPoint();
        xPosition[i] = p.x;
        yPosition[i] = p.y;
        zPosition[i] = p.z;
    }

    MergeSort::sort(xPosition, 0, n);
    MergeSort::sort(yPosition, 0, n);
    MergeSort::sort(zPosition, 0, n);

    return Point(  (xPosition[0] + xPosition[n-1])/2.0, (yPosition[0] + yPosition[n-1])/2.0, (zPosition[0] + zPosition[n-1])/2.0  );
}

void Face::scale(double _s){
    std::vector<Vertex *> vertices;
    HalfEdge *he = loop->getHERing();
    do{
        vertices.push_back(he->getStart());
        he = he->getNext();
    }while( he != loop->getHERing() );

    Point p = getBoundingBoxCenter(vertices), v;
    int n = vertices.size();
    for(int i = 0; i < n; i++){
        v = vertices.at(i)->getPoint();
        v = v + ((v - p).normalization()*_s);
        vertices.at(i)->setPoint( v );
    }

//    if(isBezier()) scaleBezier();
}

void Face::translate(Point _t){
    std::vector<Vertex *> vertices;
    HalfEdge *he = loop->getHERing();
    do{
        vertices.push_back(he->getStart());
        he = he->getNext();
    }while( he != loop->getHERing() );

    Point v;
    int n = vertices.size();
    for(int i = 0; i < n; i++){
        v = vertices.at(i)->getPoint();
        v = v + _t;
        vertices.at(i)->setPoint( v );
    }

    //if(isBezier()) resetBezier();
}

double Face::area(){
    double area = 0;

    if(isBezier()){
        Point q1, q2, q3, q4;
        int size = bezier->getSizePoints();

        for(int v = 0; v < size - 1; v++){
            for(int u = 0; u < size-1; u++){
                q1 = bezier->getPoint(u, v);
                q2 = bezier->getPoint(u, v+1);
                q3 = bezier->getPoint(u+1, v+1);
                q4 = bezier->getPoint(u+1, v);

                area += q1.area( q2, q4 );
                area += q3.area( q2, q4 );
            }
        }
    }
    else{
        HalfEdge *he1 = loop->getHERing(), *he2 = he1->getNext();
        Point p = he1->getStart()->getPoint();

        Point p1, p2;
        do{
            p1 = he2->getStart()->getPoint();
            p2 = he2->getNext()->getStart()->getPoint();
            area += p.area( p1, p2 );
            he2 = he2->getNext();
        }while(he1 != he2->getNext());
    }
    return area;
}

void Face::select(bool _s){
    selected = _s;
}

bool Face::isSelected(){
    return selected;
}

bool Face::isBezier(){
    if (bezier == NULL)
        return false;
    return true;
}
